package com.devunion.bogdan.view;

import org.apache.commons.lang.StringUtils;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;

import com.devunion.bogdan.view.editors.EditorHolder;
import com.devunion.bogdan.view.editors.DefaultEditorHolder;
import com.devunion.bogdan.view.editors.AnnotationEditor;
import com.devunion.bogdan.view.editors.PageEditorHelper;
import com.devunion.bogdan.view.event.ProjectTreeBuilderListener;
import com.devunion.bogdan.view.event.ProjectTreeBuilderEvent;
import com.devunion.bogdan.view.project.ProjectTreeBrowser;
import com.devunion.bogdan.view.project.ProjectTreeBuilder;
import com.devunion.bogdan.view.project.PageNode;
import com.devunion.bogdan.view.project.ProjectRootNode;
import com.devunion.bogdan.view.toolbar.ToolbarManager;
import com.devunion.bogdan.view.dialogs.OntologyDialog;
import com.devunion.bogdan.view.util.DialogHelper;
import com.devunion.bogdan.model.project.*;
import com.devunion.bogdan.model.UserManager;
import com.devunion.bogdan.model.event.UserListener;
import com.devunion.bogdan.model.event.UserEvent;
import com.devunion.bogdan.services.SearchService;

import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;

/**
 * @author Viktar Liaskovich
 */
public class BasicWorkspace extends JPanel implements Workspace {

    private static final String CONST_SIMILAR_PROJECTS_PANEL = "CONST_SIMILAR_PROJECTS_PANEL";
    private static final String CONST_INFO_PANEL = "CONST_INFO_PANEL";
    private static final String CONST_EDITOR_PANEL = "CONST_EDITOR_PANEL";

    private PaperOpeningListener paperOpeningListener;
    
    private EditorHolder editorHolder;
    private AnnotationEditor annotationEditor;
    private OntologyEditor ontologyEditor;
    private ProjectTreeBrowser projectTreeBrowser;
    private PaperSelectionListener selectionListener = new PaperSelectionListener();
    private JPanel jPanelRightCardContainer = new JPanel(new CardLayout());

    private JSplitPane jSplitPaneVer = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    private InfoPanel infoPanel = new InfoPanel();
    private SimilarProjectsPanel similarProjectsPanel = new SimilarProjectsPanel();
    private JSplitPane jSplitPaneHor;

    private Project project;


    public BasicWorkspace(Project project) {
        super(new BorderLayout());

        this.project = project;

        jPanelRightCardContainer.add(jSplitPaneVer, CONST_EDITOR_PANEL);
        jPanelRightCardContainer.add(infoPanel, CONST_INFO_PANEL);
        jPanelRightCardContainer.add(similarProjectsPanel, CONST_SIMILAR_PROJECTS_PANEL);

        editorHolder = new DefaultEditorHolder();
        annotationEditor = new AnnotationEditor();

        PageEditorHelper.getInstance().setEditor(editorHolder);

        JSplitPane jSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

        projectTreeBrowser = new ProjectTreeBrowser();
        jSplitPane.setTopComponent(projectTreeBrowser);

        ontologyEditor = new OntologyEditor();
        jSplitPane.setBottomComponent(ontologyEditor);
        jSplitPane.setDividerLocation(250);

        jSplitPaneHor = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        jSplitPaneHor.setLeftComponent(jSplitPane);
        jSplitPaneHor.setRightComponent(jPanelRightCardContainer);

        JScrollPane sp = new JScrollPane((Component) editorHolder);
        sp.getVerticalScrollBar().setUnitIncrement(10);
        sp.getHorizontalScrollBar().setUnitIncrement(7);
        jSplitPaneVer.setTopComponent(sp);
        jSplitPaneVer.setBottomComponent(annotationEditor);
        jSplitPaneVer.setVisible(false);

        add(jSplitPaneHor);

        selectionListener = new PaperSelectionListener();

        ProjectTreeBuilder.getInstance().addProjectTreeBuilderListener(new ProjectTreeBuilderListener() {
            public void onTreeCreated(ProjectTreeBuilderEvent e) {

                final JTree projectTree = e.getProjectTree();

                projectTree.addTreeSelectionListener(selectionListener);

                if (projectTree.getSelectionPath() == null) {
                    projectTree.setSelectionPath(new TreePath(((DefaultMutableTreeNode) projectTree.getModel().getRoot()).getPath()));
                }
            }

            public void onTreeDestroyed(ProjectTreeBuilderEvent e) {
                final JTree projectTree = e.getProjectTree();

                projectTree.removeTreeSelectionListener(selectionListener);

                editorHolder.onPorjectClose();
            }
        });
    }

    public EditorHolder getEditorHolder() {
        return editorHolder;
    }

    public void onActive() {
        jSplitPaneHor.setDividerLocation(0.25d);
    }

    public void onSelected() {
        final JTree projectTree = LibrarianEditor.getInstance().getSelectedWorkspace().getProjectTreeBrowser().getProjectTree();

        if (projectTree == null) {
            return;
        }

        Object lastPathComponent = projectTree.getLastSelectedPathComponent();

        if (lastPathComponent instanceof PageNode) {
            annotationEditor.clear();

            PageNode node = (PageNode) lastPathComponent;
            editorHolder.setPage(node.getUserObject());

            jSplitPaneVer.setVisible(true);
            ((CardLayout) jPanelRightCardContainer.getLayout()).show(jPanelRightCardContainer, CONST_EDITOR_PANEL);
            ToolbarManager.getInstance().setEditorToolbarVisible(true);
            jSplitPaneVer.setDividerLocation(0.85d);
        }
    }

    private class PaperSelectionListener implements TreeSelectionListener {
        public void valueChanged(TreeSelectionEvent e) {
            if (e.getNewLeadSelectionPath() == null) {
                return;
            }

            Object lastPathComponent = e.getNewLeadSelectionPath().getLastPathComponent();

            if (lastPathComponent instanceof PageNode) {
                annotationEditor.clear();

                PageNode node = (PageNode) lastPathComponent;
                editorHolder.setPage(node.getUserObject());

                jSplitPaneVer.setVisible(true);
                ((CardLayout) jPanelRightCardContainer.getLayout()).show(jPanelRightCardContainer, CONST_EDITOR_PANEL);
                ToolbarManager.getInstance().setEditorToolbarVisible(true);
                jSplitPaneVer.setDividerLocation(0.85d);
            } else if (lastPathComponent instanceof ProjectRootNode) {
                jSplitPaneVer.setVisible(false);

                ((CardLayout) jPanelRightCardContainer.getLayout()).show(jPanelRightCardContainer, CONST_SIMILAR_PROJECTS_PANEL);
                similarProjectsPanel.initialize((Project) ((DefaultMutableTreeNode) lastPathComponent).getUserObject());
                ToolbarManager.getInstance().setEditorToolbarVisible(false);
            } else {
                jSplitPaneVer.setVisible(false);

                ((CardLayout) jPanelRightCardContainer.getLayout()).show(jPanelRightCardContainer, CONST_INFO_PANEL);
                infoPanel.setObject(((DefaultMutableTreeNode) lastPathComponent).getUserObject());
                ToolbarManager.getInstance().setEditorToolbarVisible(false);
            }
        }
    }

    private class InfoPanel extends JPanel {
        private JLabel jLabelInfo = new JLabel();

        public InfoPanel() {
            super(new BorderLayout(14, 14));

            add(jLabelInfo, BorderLayout.NORTH);
        }

        public void setObject(Object o) {
            jLabelInfo.setText(getInfo(o));
        }

        private String getInfo(Object o) {
            return o.toString();
        }
    }

    private class SimilarProjectsPanel extends JPanel {
        private JTable jTableProjects;
        private boolean initialized = false;

        public SimilarProjectsPanel() {
            super(new BorderLayout(14, 14));

        }

        public void initialize(Project project) {
            if (!initialized) {

                final DefaultTableModel model = new DefaultTableModel(new Object[][] {}, new String[] {"Project name"});
                jTableProjects = new JTable(model) {
                    @Override
                    public boolean isCellEditable(int row, int column) {
                        return false;
                    }
                };

                jTableProjects.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
                            int row = ((JTable) e.getSource()).getSelectedRow();

                            final Object proj = model.getValueAt(row, 0);

                            if (paperOpeningListener != null) {
                                paperOpeningListener.openProject((Project) proj);
                            }
                        }
                    }
                });

                JScrollPane scrollPane = new JScrollPane(jTableProjects);
                jTableProjects.setFillsViewportHeight(true);

                add(scrollPane);

                final List<Project> projects = SearchService.getInstance().findSimilar(project);

                for (Project p : projects) {
                    model.addRow(new Object[] {p});
                }

                initialized = true;
            }
        }
    }

    public Project getProject() {
        return project;
    }

    private class OntologyEditor extends JPanel {
        private JTable jTableOntologies;
        private OntologyDialog ontologyDialog;
        private JButton jButtonEdit;
        private JButton jButtonRemove;

        private OntologyEditor() {
            super(new BorderLayout());

            initComponenets();
        }


        private Object[] createTR(Ontology o) {
            return new Object[]{o.getText(), createProperties(o.getOntologyProperties())};
        }

        private String createProperties(List<OntologyProperty> properties) {

            StringBuilder values = new StringBuilder();
            for (int i = 0; i < properties.size(); i++) {
                OntologyProperty p = properties.get(i);

                values.append(p.getName());
                values.append(" : ");
                values.append(StringUtils.join(p.getValues().iterator(), ","));

                if (i != properties.size() - 1) {
                    values.append(";");
                }
            }

            return values.toString();
        }

        private void initComponenets() {
            add(new JLabel("Project ontologies:"), BorderLayout.NORTH);

            final DefaultTableModel model = new DefaultTableModel(new Object[][]{}, new String[]{"Text", "Properties"});
            final List<Ontology> ontologies = project.getOntologies();

            if (ontologies != null) {
                for (Ontology o : ontologies) {
                    model.addRow(createTR(o));
                }
            }

            // TODO: can't add another OP when one property already exists
            // Can't add empty ontology

            jTableOntologies = new JTable(model) {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false;
                }
            };

            JScrollPane scrollPane = new JScrollPane(jTableOntologies);
            jTableOntologies.setFillsViewportHeight(true);

            add(scrollPane, BorderLayout.CENTER);

            JPanel jPanelButtons = new JPanel(new FlowLayout(FlowLayout.CENTER));
            final JButton jButtonAdd = new JButton("Add");

            User u = UserManager.getInstance().getUser();
            if (u == null || u.getRole() == UserRole.NORMAL) {
                jButtonAdd.setEnabled(false);
            }

            jButtonAdd.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (ontologyDialog == null) {
                        ontologyDialog = new OntologyDialog(DialogHelper.getMainFrame());
                    }

                    ontologyDialog.setOntology(null);

                    ontologyDialog.pack();
                    ontologyDialog.setLocationRelativeTo(null);
                    ontologyDialog.setVisible(true);

                    final Ontology ontology = ontologyDialog.getOntology();

                    if (ontology == null) {
                        return;
                    }

                    model.addRow(createTR(ontology));

                    project.addOntology(ontology);
                }
            });
            jPanelButtons.add(jButtonAdd);

            jButtonRemove = new JButton("Remove");
            jButtonRemove.setEnabled(false);
            jButtonRemove.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    int row = jTableOntologies.getSelectionModel().getLeadSelectionIndex();
                    final String prop = (String) model.getValueAt(row, 0);

                    final int res = JOptionPane.showConfirmDialog(OntologyEditor.this, "Are you sure to remove selected ontology?", "Remove ontology", JOptionPane.YES_NO_OPTION);

                    if (res == JOptionPane.YES_OPTION) {
                        model.removeRow(row);

                        final Ontology o = findOntology(prop);

                        if (o != null) {
                            project.removeOntology(o);
                        }
                    }
                }
            });
            jPanelButtons.add(jButtonRemove);

            jButtonEdit = new JButton("Edit");
            jButtonEdit.setEnabled(false);
            jButtonEdit.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (ontologyDialog == null) {
                        ontologyDialog = new OntologyDialog(DialogHelper.getMainFrame());
                    }

                    int row = jTableOntologies.getSelectionModel().getLeadSelectionIndex();

                    final String prop = (String) model.getValueAt(row, 0);
                    final Ontology ontology = findOntology(prop);

                    ontologyDialog.setOntology(ontology);

                    ontologyDialog.pack();
                    ontologyDialog.setLocationRelativeTo(null);
                    ontologyDialog.setVisible(true);

                    final Ontology o = ontologyDialog.getOntology();

                    if (o == null) {
                        return;
                    }

                    model.setValueAt(o.getText(), row, 0);
                    model.setValueAt(createProperties(o.getOntologyProperties()), row, 1);
                }
            });
            jPanelButtons.add(jButtonEdit);

            add(jPanelButtons, BorderLayout.SOUTH);

            jTableOntologies.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
            jTableOntologies.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    if (e.getValueIsAdjusting()) {
                        return;
                    }

                    updateOnSelection(UserManager.getInstance().getUser(), e.getLastIndex());
                }
            });

            UserManager.getInstance().addUserListener(new UserListener() {
                public void userLoggedIn(UserEvent userEvent) {
                    final User u = userEvent.getUser();
                    if (u == null || u.getRole() == UserRole.NORMAL) {
                        jButtonAdd.setEnabled(false);
                        jButtonEdit.setEnabled(false);
                        jButtonRemove.setEnabled(false);
                    } else {
                        jButtonAdd.setEnabled(true);

                        int row = jTableOntologies.getSelectionModel().getLeadSelectionIndex();
                        updateOnSelection(u, row);
                    }
                }
            });
        }

        private void updateOnSelection(User u, int index) {
            if (index == -1) {
                return;
            }

            if (u == null || u.getRole() == UserRole.NORMAL) {
                jButtonEdit.setEnabled(false);
                jButtonRemove.setEnabled(false);
            } else if (u.getRole() == UserRole.EXPERT || u.getRole() == UserRole.ADMIN) {
                boolean enable = jTableOntologies.getRowCount() != 0;

                jButtonEdit.setEnabled(enable);
                jButtonRemove.setEnabled(enable);
            }
        }

        private Ontology findOntology(String prop) {
            for (Ontology o : project.getOntologies()) {
                if (o.getText().equals(prop)) {
                    return o;
                }
            }

            return null;
        }
    }

    public ProjectTreeBrowser getProjectTreeBrowser() {
        return projectTreeBrowser;
    }

    public void setPaperOpeningListener(PaperOpeningListener paperOpeningListener) {
        this.paperOpeningListener = paperOpeningListener;
    }
}
